`timescale    1ps/1ps
module v8_com_ctrl_01
#(parameter
    DEVICE_TYPE = 0    //0：接收卡   1：附属设备  当作为接收卡或者附属设备时，解析命令位置会发生变化，具体见协议
)
(
    input  wire        resetb,
    input  wire        sclk,
    input  wire        comm_en,

    input  wire        rec_flag,
    input  wire        rec_error,
    input  wire [7:0]  rec_data,
    input  wire [1:0]  rec_vendor, //0:无效包 1：UDP包
    
    output reg         send_flag,
    output reg         pre_flag,
    output wire [7:0]  send_data,
    
    input  wire        blank_flag,
    input  wire        redu_flag,
    input  wire        time_1ms_sync,
           
    output wire        fpga_rec_flag,
    output wire        fpga_send_flag,    
    output reg         op_start_flag,
    output reg  [31:0] op_start_addr,
    output wire [7:0]  op_length,
    input  wire        op_ack,

    input  wire        fpga_rec_end,
    input  wire [7:0]  rec_buf_raddr,
    output wire [7:0]  rec_buf_rdata,

    input  wire        fpga_send_end,
    input  wire        send_buf_we,
    input  wire [7:0]  send_buf_waddr,
    input  wire [7:0]  send_buf_wdata,

    output wire [7:0]  tout
);

//************************************************/
//        !!设备类型!!
//************************************************/

//************************************************/
//        参数定义
//************************************************/
//************************************************/
localparam  Preamble_OFFSET    = 23;
localparam  PACK_TYPE_FIRST    = 24 - 1;
localparam  ASI_DEPTH_FIRST    = 26 - 1;
localparam  PACK_DEPTH_FIRST   = 28 - 1;
localparam  COM_PACK_SEL_TYPE  = 30 - 1;
localparam  COM_PACK_SEL_ADDR  = 31 - 1;
localparam  COM_PACK_ASI_ADDR  = 33 - 1;
localparam  COM_PACK_COMMAND   = 38 - 1;
localparam  COM_PACK_ADDR      = 40 - 1;
localparam  COM_PACK_HEAD_END  = 43 - 1;

localparam  TIMEOUT            = 15;

//*************state******************************
localparam  IDLE_STATE         = 6'h01;
localparam  WAIT_STATE         = 6'h02;
localparam  COM_RECEIVE        = 6'h04;
localparam  FPGA_RECEIVE       = 6'h08;
localparam  FPGA_SEND          = 6'h10;
localparam  SEND_FEEDBACK      = 6'h20;

///**************命令类型****************
localparam  MEM_READ           = 8'h08;
localparam  MEM_WRITE          = 8'h04;

//**********************************************/
//        信号定义
/************************************************/
reg         buf_wen;
reg  [10:0] buf_addr;
reg  [7:0]  buf_wdata;
wire [7:0]  buf_rdata;

reg  [5:0]  com_state;
reg  [8:0]  data_count;
reg         head_flag,head_end,com_packet_flag;
reg  [7:0]  current_depth_c, current_depth;
reg         device_active,read_command,write_command,data_end;
reg  [23:0] op_page_addr;
reg         com_rec_temp,send_flag_a,send_flag_b;
reg         rec_data_wen;
reg         buf_wen_a,buf_wen_b;
reg  [9:0]  buf_addr_a;
wire [9:0]  buf_addr_b;
reg  [1:0]  ack_head_type_t1,ack_head_type;
reg         udp_head,udp_head_end;
reg         fpga_send_end_flag;
reg         send_timeout;
reg  [3:0]  ms_count;
wire [2:0]  current_device;

//*************************************************/
//        通讯buf
//*************************************************/    
//com_buf 140516修改 UDP时desport为5AA5
swsr_1k8_tp_com_mif    com_buf (
    .clock_a ( sclk ),
    .wren_a ( buf_wen_a ),
    .address_a ( buf_addr_a ),
    .data_a ( send_buf_wdata ),
    .q_a ( buf_rdata ),
    
    .clock_b ( sclk ),
    .address_b ( buf_addr_b ),
    .wren_b ( buf_wen_b ),
    .data_b ( rec_data ),
    .q_b ()
    );

//************************************************/
//        状态控制
//************************************************/
//**************主状态机*******************
always@(posedge sclk or negedge resetb)
    if(resetb == 0)
        com_state <= IDLE_STATE;
    else 
        case(com_state)
            IDLE_STATE:    
                if (comm_en==0)
                    com_state <= WAIT_STATE;    
                else if (rec_flag==1)
                    com_state <= COM_RECEIVE;    
            WAIT_STATE:
                if(rec_flag==0 && comm_en==1)
                    com_state <= IDLE_STATE;
            COM_RECEIVE:
                if (rec_flag==0) begin
                    if(rec_error==1 || com_packet_flag==0 || device_active==0)
                        com_state <= IDLE_STATE;
                    else if (write_command==1)
                        com_state <= FPGA_RECEIVE;
                    else if (read_command==1)
                        com_state <= FPGA_SEND;
                    else
                        com_state <= IDLE_STATE;
                    end
            FPGA_RECEIVE:
                if(fpga_rec_end == 1 || no_ack == 1)
                    com_state <= WAIT_STATE;
            FPGA_SEND:
                if(fpga_send_end_flag==1 || no_ack == 1)
                begin
                    if(redu_flag==0)    //单向级联模式
                        com_state <= SEND_FEEDBACK;
                    else if(blank_flag==1 || send_timeout==1)    //双口冗余模式，等空闲包
                        com_state <= SEND_FEEDBACK;
                end
            SEND_FEEDBACK:
                if(data_end == 1)
                    com_state <= WAIT_STATE;
            default:    com_state <= WAIT_STATE;
        endcase

//**************通讯包有效数据计数*******************
//包头标志
always @(posedge sclk)
    if (com_state==IDLE_STATE || com_state==FPGA_SEND)
        head_flag<=1;
    else if (head_end==1)
        head_flag<=0;

//包头结束标志
always @(posedge sclk)
    if (head_flag==1 && data_count==COM_PACK_HEAD_END-1 && udp_head==0)
        head_end<=1;
    else
        head_end<=0;

//数据计数
always @(posedge sclk)
    if (com_state==IDLE_STATE || udp_head_end==1)
        data_count<=23;
    else if (head_end==1 )//|| com_state==FPGA_SEND)
        data_count<=0;
    else if(com_state==FPGA_SEND) begin
        if(redu_flag==1)
            data_count<=44;    //去掉部分包头，用于冗余替换空闲包数据
        else
            data_count<=0;
        end
    else if (com_state==COM_RECEIVE || com_state==SEND_FEEDBACK)
        data_count<=data_count+1'b1;

always @(posedge sclk or negedge resetb)
    if(resetb==0)
        udp_head<=0;
    else if(com_state==FPGA_SEND && ack_head_type==1) 
        udp_head<=1'b1;
    else if(udp_head_end==1)
        udp_head<=0;

always @(posedge sclk or negedge resetb)
    if(resetb==0)
        udp_head_end<=0;
    else if(com_state==SEND_FEEDBACK && udp_head==1 && data_count==49)
        udp_head_end<=1'b1;
    else
        udp_head_end<=0;
//**************通讯包解码*******************
//通讯包识别
assign current_device=DEVICE_TYPE;

always @(posedge sclk)
    if (com_state!=COM_RECEIVE)
        com_packet_flag<=0;
    else if (head_flag==1 && data_count[5:0]==PACK_TYPE_FIRST && rec_data==8'hC5 && rec_vendor==1)
        com_packet_flag<=1'b1;

//设备级联编号识别
always @(posedge sclk)
    if (com_state!=COM_RECEIVE)
        current_depth_c<=0;
    else if (current_device==0 && com_state==COM_RECEIVE && head_flag==1 && data_count[5:0]==PACK_DEPTH_FIRST)//接收卡级联深度
        current_depth_c<=rec_data;
    else if (current_device==1 && com_state==COM_RECEIVE && head_flag==1 && data_count[5:0]==ASI_DEPTH_FIRST)//附属设备级联深度
        current_depth_c<=rec_data;
        
//设备编号选择
always @(posedge sclk)
    current_depth <= current_depth_c;
    
//设备选择识别
always @(posedge sclk)
    if (com_state!=COM_RECEIVE)
        device_active<=0;
    else if(current_device==0)    //接收卡
    begin
        if (head_flag==1 && data_count[5:0]==COM_PACK_SEL_TYPE && rec_data[0]==1)
            device_active<=1'b1;
        else if (head_flag==1 && data_count[5:0]==COM_PACK_SEL_ADDR && rec_data==current_depth && current_depth!=0)
            device_active<=1'b1;
    end
    else if(current_device==1)    //附属设备
    begin
        if (head_flag==1 && data_count[5:0]==COM_PACK_SEL_TYPE && rec_data[1]==1)
            device_active<=1'b1;
        else if (head_flag==1 && data_count[5:0]==COM_PACK_ASI_ADDR && rec_data==current_depth && current_depth!=0)
            device_active<=1'b1;
    end
    
always @(posedge sclk)
    if (com_state!=COM_RECEIVE)
        read_command<=0;
    else if (head_flag==1 && data_count[5:0]==COM_PACK_COMMAND)
        read_command<=rec_data[3];

always @(posedge sclk)
    if (com_state!=COM_RECEIVE)
        write_command<=0;
    else if (head_flag==1 && data_count[5:0]==COM_PACK_COMMAND)
        write_command<=rec_data[2];

always    @(posedge sclk or negedge resetb)
    if (resetb==0)
        op_page_addr<=0;
    else if (com_state==COM_RECEIVE && head_flag==1 && com_packet_flag==1)
        case(data_count)
            COM_PACK_ADDR+1 :  op_page_addr[7:0]    <=rec_data;
            COM_PACK_ADDR+2 :  op_page_addr[15:8]    <=rec_data;
            COM_PACK_ADDR+3 :  op_page_addr[23:16]    <=rec_data;
        endcase

/***************应答包长度控制**********************/
always @(posedge sclk)
    if (data_count==255-1)
        data_end<=1'b1;
    else
        data_end<=0;

reg    com_packet_t;
reg    [5:0]    com_state_t;
always @(posedge sclk)
    begin
        com_packet_t<=com_packet_flag;
        com_state_t<=com_state;
    end
    
always @(posedge sclk or negedge resetb)
    if(resetb==0)
        ack_head_type_t1<=0;
    else if (com_packet_t==0 && com_packet_flag==1)
        ack_head_type_t1<=rec_vendor;

always @(posedge sclk or negedge resetb)
    if(resetb==0)
        ack_head_type<=0;
    else if(com_state_t==COM_RECEIVE && com_state==FPGA_SEND)
        ack_head_type<=ack_head_type_t1;

//**********************************************************************/
//        给内部接口模块信号
//**********************************************************************/    
assign op_length    =255;

always @(posedge sclk or negedge resetb)
    if(resetb==0)
        op_start_addr<=0;
    else 
    case(op_page_addr[23:8])
        'h00C0:             op_start_addr<={12'h004,4'h0,op_page_addr[7:0],8'h0}; // BootLoader
        'h00C1:             op_start_addr<={12'h004,4'h1,op_page_addr[7:0],8'h0};
        'h00C2:             op_start_addr<={12'h004,4'h2,op_page_addr[7:0],8'h0};
        'h00C3:             op_start_addr<={12'h004,4'h3,op_page_addr[7:0],8'h0}; // Test
        'h00C4:             op_start_addr<={12'h004,4'h4,op_page_addr[7:0],8'h0}; // Lock period
        'h00C5:             op_start_addr<={12'h004,4'h5,op_page_addr[7:0],8'h0};
        'h00C6:             op_start_addr<={12'h004,4'h6,op_page_addr[7:0],8'h0}; // Normal
        'h00C7:             op_start_addr<={12'h004,4'h7,op_page_addr[7:0],8'h0};
        'h00C8:             op_start_addr<={12'h004,4'h8,op_page_addr[7:0],8'h0};
        'h00C9:             op_start_addr<={12'h004,4'h9,op_page_addr[7:0],8'h0};
        'h00CA:             op_start_addr<={12'h004,4'hA,op_page_addr[7:0],8'h0};
        'h00CB:             op_start_addr<={12'h004,4'hB,op_page_addr[7:0],8'h0};

        'h00D0:             op_start_addr<={12'h800,4'h0,op_page_addr[7:0],8'h0}; // BootLoader 重定义
        'h00D1:             op_start_addr<={12'h800,4'h1,op_page_addr[7:0],8'h0};
        'h00D2:             op_start_addr<={12'h800,4'h2,op_page_addr[7:0],8'h0};
        'h00D3:             op_start_addr<={12'h800,4'h3,op_page_addr[7:0],8'h0}; // 
        'h00D4:             op_start_addr<={12'h005,4'h4,op_page_addr[7:0],8'h0}; // Lock period
        'h00D5:             op_start_addr<={12'h800,4'h5,op_page_addr[7:0],8'h0};
        'h00D6:             op_start_addr<={12'h005,4'h6,op_page_addr[7:0],8'h0}; // Normal
        'h00D7:             op_start_addr<={12'h005,4'h7,op_page_addr[7:0],8'h0};
        'h00D8:             op_start_addr<={12'h005,4'h8,op_page_addr[7:0],8'h0};
        'h00D9:             op_start_addr<={12'h005,4'h9,op_page_addr[7:0],8'h0};
        'h00DA:             op_start_addr<={12'h005,4'hA,op_page_addr[7:0],8'h0};
        'h00DB:             op_start_addr<={12'h005,4'hB,op_page_addr[7:0],8'h0};
       
        {12'h004,4'h0}:     op_start_addr<={12'h800,4'h0,op_page_addr[7:0],8'h0}; //操作地址为0x0040_0000~0x004B_FFFF时(被重定义）  
        {12'h004,4'h1}:     op_start_addr<={12'h800,4'h1,op_page_addr[7:0],8'h0};
        {12'h004,4'h2}:     op_start_addr<={12'h800,4'h2,op_page_addr[7:0],8'h0};
        {12'h004,4'h3}:     op_start_addr<={12'h800,4'h3,op_page_addr[7:0],8'h0};
        {12'h004,4'h4}:     op_start_addr<={12'h800,4'h4,op_page_addr[7:0],8'h0};
        {12'h004,4'h5}:     op_start_addr<={12'h800,4'h5,op_page_addr[7:0],8'h0};
        {12'h004,4'h6}:     op_start_addr<={12'h800,4'h6,op_page_addr[7:0],8'h0};
        {12'h004,4'h7}:     op_start_addr<={12'h800,4'h7,op_page_addr[7:0],8'h0};
        {12'h004,4'h8}:     op_start_addr<={12'h800,4'h8,op_page_addr[7:0],8'h0};
        {12'h004,4'h9}:     op_start_addr<={12'h800,4'h9,op_page_addr[7:0],8'h0};
        {12'h004,4'hA}:     op_start_addr<={12'h800,4'hA,op_page_addr[7:0],8'h0};
        {12'h004,4'hB}:     op_start_addr<={12'h800,4'hB,op_page_addr[7:0],8'h0};

        {12'h005,4'h0}:     op_start_addr<={12'h800,4'h0,op_page_addr[7:0],8'h0}; //操作地址为0x0050_0000~0x005B_FFFF时(被重定义）  
        {12'h005,4'h1}:     op_start_addr<={12'h800,4'h1,op_page_addr[7:0],8'h0};
        {12'h005,4'h2}:     op_start_addr<={12'h800,4'h2,op_page_addr[7:0],8'h0};
        {12'h005,4'h3}:     op_start_addr<={12'h800,4'h3,op_page_addr[7:0],8'h0};
        {12'h005,4'h4}:     op_start_addr<={12'h800,4'h4,op_page_addr[7:0],8'h0};
        {12'h005,4'h5}:     op_start_addr<={12'h800,4'h5,op_page_addr[7:0],8'h0};
        {12'h005,4'h6}:     op_start_addr<={12'h800,4'h6,op_page_addr[7:0],8'h0};
        {12'h005,4'h7}:     op_start_addr<={12'h800,4'h7,op_page_addr[7:0],8'h0};
        {12'h005,4'h8}:     op_start_addr<={12'h800,4'h8,op_page_addr[7:0],8'h0};
        {12'h005,4'h9}:     op_start_addr<={12'h800,4'h9,op_page_addr[7:0],8'h0};
        {12'h005,4'hA}:     op_start_addr<={12'h800,4'hA,op_page_addr[7:0],8'h0};
        {12'h005,4'hB}:     op_start_addr<={12'h800,4'hB,op_page_addr[7:0],8'h0};

        default:            op_start_addr<={op_page_addr,8'h0};               //其他为原地址
    endcase
                   
assign fpga_rec_flag    =com_state[3];
assign fpga_send_flag    =com_state[4];

always @(posedge sclk)
    com_rec_temp<=com_state[2];

always @(posedge sclk)
    if ((fpga_rec_flag==1 || fpga_send_flag==1) && com_rec_temp==1)
        op_start_flag<=1'b1;
    else
        op_start_flag<=0;

//**********************************************************************/
//        响应检测
//**********************************************************************/
reg  [3:0]  op_ack_count;
reg         no_ack;

//ack检测延时
always @(posedge sclk or negedge resetb)
    if (resetb==0)
        op_ack_count <= 0;
    else if (op_start_flag == 1)
        op_ack_count <= 0;
    else if (op_ack_count[3] == 1'b0)
        op_ack_count <= op_ack_count + 1'b1;
    
//无模块响应标志
always @(posedge sclk or negedge resetb)
    if (resetb==0)
        no_ack <= 0;
    else if (com_rec_temp == 1)            //通讯接收态，清除无反馈标志
        no_ack <= 0;
    else if (op_ack_count == 6 && op_ack == 0)    //根据外部响应设置无反馈标志
        no_ack <= 1'b1;

//**********************************************************************/
//        应答包发送
//**********************************************************************/
always @(posedge sclk or negedge resetb)
    if (resetb==0)
        send_flag<= 0;
    else if (com_state==SEND_FEEDBACK)
        send_flag<= 1'b1;
    else
        send_flag<=0;
    
always @*
    if (com_state==SEND_FEEDBACK && head_flag==1 && data_count>=1 && data_count<=8)
        pre_flag<= 1'b1;
    else
        pre_flag<= 0;

assign send_data=buf_rdata;

//**********************************************************************/
//        内部BUF控制
//**********************************************************************/    
//*****************写控制*****************
always @(posedge sclk)
    if (com_state!=COM_RECEIVE)
        rec_data_wen<= 0;
    else if (data_count==COM_PACK_SEL_TYPE-1)
        rec_data_wen<= 1'b1;
    else if (data_end==1)
        rec_data_wen<= 0;
                
always @*    
    if(com_state==FPGA_SEND && send_buf_we==1)
        buf_wen_a<=1'b1;
    else
        buf_wen_a<=0;
        
always @*
    buf_wen_b<=rec_data_wen;

always @*
    if (com_state==FPGA_RECEIVE)
        buf_addr_a = {2'b00,rec_buf_raddr}; 
    else if(com_state==FPGA_SEND)
        buf_addr_a = {2'b01,send_buf_waddr};    
    else if(udp_head==1)
        buf_addr_a = {2'b10,1'b1,data_count[6:0]};
    else if(head_flag==1)
        buf_addr_a = {2'b10,data_count[7:0]};
    else if(com_state==SEND_FEEDBACK)
        buf_addr_a = {2'b01,data_count[7:0]};
    else
        buf_addr_a = 'd0;

assign buf_addr_b={head_flag,1'b0,data_count[7:0]};
                        
assign rec_buf_rdata=buf_rdata;

/************************************************/
//        超时反馈控制
/************************************************/
always @(posedge sclk)
    if(com_state!=FPGA_SEND)
        fpga_send_end_flag<=0;
    else if(fpga_send_end==1)
        fpga_send_end_flag<=1'b1;

always @(posedge sclk or negedge resetb)
    if(resetb==0)
        ms_count<=0;
    else if(time_1ms_sync==1)begin
        if(fpga_send_end_flag==0)
            ms_count<=0;
        else if(ms_count==TIMEOUT-1)
            ms_count<=0;
        else
            ms_count<=ms_count+1'b1;
    end
        
always @(posedge sclk)
    if(time_1ms_sync==1 && ms_count==TIMEOUT-1)
        send_timeout<=1'b1;
    else
        send_timeout<=0;

/************************************************/
//        测试信号
/************************************************/
assign    tout=0;

endmodule        

